<?php

// Copyright 2019 Hackware SpA <human@hackware.cl>
// Part of "Hackware Web Services Core", released under the MIT License terms.

namespace Hawese\Tests;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Illuminate\Validation\ValidationException;
use Hawese\Core\Exceptions
    \{Handler, JsonExceptionRenderer, ModelValidationException,
    ModelObjectNotFoundException, UnknownForeignObjectException,
    WrongCredentialsException};
use Exception;

class ExceptionsTest extends TestCase
{
    public function testBuildCode()
    {
        $renderer = new JsonExceptionRenderer(new Exception());
        $this->assertArrayNotHasKey('code', $renderer->build());

        $renderer = new JsonExceptionRenderer(new Exception('', 42));
        $this->assertSame(42, $renderer->build()['code']);
    }

    public function testBuildMessage()
    {
        $renderer = new JsonExceptionRenderer(
            (new HttpException(
                Response::HTTP_BAD_REQUEST
            ))
        );
        $this->assertSame(
            Response::$statusTexts[Response::HTTP_BAD_REQUEST],
            $renderer->build()['message']
        );

        $renderer = new JsonExceptionRenderer(
            (new HttpException(
                Response::HTTP_BAD_REQUEST,
                'Custom message'
            ))
        );
        $this->assertSame(
            'Custom message',
            $renderer->build()['message']
        );
    }

    public function testBuildDetail()
    {
        $renderer = new JsonExceptionRenderer(new Exception());
        $this->assertArrayNotHasKey('detail', $renderer->build());

        $renderer = new JsonExceptionRenderer(
            new UnknownForeignObjectException('model', 'attribute')
        );
        $this->assertEqualsCanonicalizing(
            ['model' => 'model', 'attribute' => 'attribute'],
            $renderer->build()['detail']
        );

        // ValidationException instance tested on testModelValidationException
    }

    public function testBuildDebug()
    {
        $renderer = new JsonExceptionRenderer((new Exception()));
        $this->assertSame('Exception', $renderer->build()['debug']['exception']);

        $_ENV['APP_DEBUG'] = false;
        $this->assertArrayNotHasKey('debug', $renderer->build());
    }

    public function testResponseAndRender()
    {
        $response = JsonExceptionRenderer::render(new Exception());
        $this->assertObjectHasAttribute('error', $response->getData());

        $this->assertSame(
            Response::HTTP_INTERNAL_SERVER_ERROR,
            $response->getStatusCode()
        );

        $response = JsonExceptionRenderer::render(
            (new HttpException(
                Response::HTTP_BAD_REQUEST
            ))
        );
        $this->assertSame(
            Response::HTTP_BAD_REQUEST,
            $response->getStatusCode()
        );
    }

    public function testModelObjectNotFoundException()
    {
        $response = JsonExceptionRenderer::render(
            new ModelObjectNotFoundException('model', 'key', 'value')
        );
        $this->assertSame(
            'model key value could not be found',
            $response->getData()->error->message
        );
        $response = JsonExceptionRenderer::render(
            new ModelObjectNotFoundException('model', ['key1', 'key2'], 'value')
        );
        $this->assertSame(
            'model key1 or key2 value could not be found',
            $response->getData()->error->message
        );
    }

    public function testModelValidationException()
    {
        $validator = app('validator')->make([], ['attr' => 'required']);
        $response = JsonExceptionRenderer::render(
            new ModelValidationException('model', $validator)
        );
        $this->assertSame(
            Response::HTTP_UNPROCESSABLE_ENTITY,
            $response->getStatusCode()
        );
        $this->assertStringContainsString(
            'Failed',
            $response->getData()->error->message
        );
        $this->assertStringContainsString(
            'required',
            $response->getData()->error->detail->attributes->attr[0]
        );
    }

    public function testUnknownForeignObjectException()
    {
        $response = JsonExceptionRenderer::render(
            new UnknownForeignObjectException('model', 'attribute')
        );
        $this->assertSame(
            Response::HTTP_BAD_REQUEST,
            $response->getStatusCode()
        );
        $this->assertEqualsCanonicalizing(
            (object) ['model' => 'model', 'attribute' => 'attribute'],
            $response->getData()->error->detail
        );
    }

    public function testWrongCredentialsException()
    {
        $response = JsonExceptionRenderer::render(
            new WrongCredentialsException('model', 'identifier')
        );
        $this->assertSame(
            Response::HTTP_FORBIDDEN,
            $response->getStatusCode()
        );
        $this->assertSame(
            'Wrong secret for model identifier',
            $response->getData()->error->message
        );
    }
}
